/*  File Name	: GateMonitor.cpp
 *  Author		: Javie Pong
 *	 E-mail		: 823960168@qq.com
 *  Created on	: 2012-7-1
 *  Description:
 */
#include "GateMonitor.h"
#include "EpollReactor.h"
#include "MySqlDB.h"


int GateMonitor::RoutineTimer::handle_timeout(void)
{
	this->monitor->register_to_dirc();
	this->monitor->send_room_info(true);
	return 0;
}

GateMonitor::GateMonitor()
{
	this->game_type_ = 0;
	this->gate_id_ = 0;
	this->acceptor_ = NULL;
	this->player_map_.clear();
	this->player_count_ = 0;
	this->engine_map_.clear();
	this->register_dirc_ = false;
	this->room_change_ = false;
	this->dircserver_ = NULL;
	this->dbserver_ = NULL;
}

GateMonitor::~GateMonitor()
{
	DELETE(this->acceptor_);

	this->player_pool_.recycle();
}

GateMonitor* GateMonitor::instance_ = NULL;

GateMonitor *GateMonitor::instance(void)
{
	if(NULL==instance_)
	{
		instance_ = new GateMonitor();
	}
	return instance_;
}

const int GateMonitor::monitor_id(void)
{
	return this->gate_id_;
}

const int GateMonitor::gate_id(void)
{
	return this->cfg()["uid"].asInt();//this->gate_id_;
}

GatePlayer *GateMonitor::find_player(int roleid)
{
	GatePlayerMap::iterator iter = this->player_map_.find(roleid);
	if(iter==this->player_map_.end())
	{
		return NULL;
	}
	return iter->second;
}

int GateMonitor::bind_player(int role_id, GatePlayer *p)
{
	//LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,-1);
	GatePlayerMap::iterator iter = this->player_map_.find(role_id);
	if (iter == this->player_map_.end())
	{
		this->player_map_.insert(std::pair<int, GatePlayer*>(role_id, p));
		return 0;
	}
	return -1;
}

int GateMonitor::unbind_player(int role_id)
{
	//LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,-1);
	GatePlayerMap::iterator iter = this->player_map_.find(role_id);
	if (iter != this->player_map_.end())
	{
		iter->second->reset();
		this->player_pool()->push(iter->second);
		this->player_map_.erase(role_id);
		return 0;
	}
	return -1;
}

const Json::Value &GateMonitor::cfg(void)
{
#ifdef DEBUG
	return CONFIG_INSTANCE->gateway()["debug"][this->line_id_];

#else
	return CONFIG_INSTANCE->gateway()["online"][this->line_id_];
#endif
}


int GateMonitor::init(int line_id,int list_size,int block_size,int pool_size,
		int growth_size,std::string pool_name)
{
	this->line_id_ = line_id;
	this->gate_id_ = this->cfg()["uid"].asInt();
	this->game_type_ = this->cfg()["gametype"].asInt();

	super::init(list_size,block_size,pool_size,growth_size,pool_name);
	this->chk_account_list_ = new DuplexList(list_size);
	if(NULL==this->chk_account_list_)
	{
		return -1;
	}

	this->acceptor_ = new GateAcceptor(this);
	if(NULL==this->acceptor_)
	{
		return -1;
	}

	this->connector_ = new GateConnector(this);
	if(NULL==this->connector_)
	{
		return -1;
	}

	//request thread
	int req_thr = this->cfg()["request_thr"].asInt();
	this->create_request_thr(req_thr,"GateMonitor::request",false);

	//respond thread
	int res_thr = this->cfg()["respond_thr"].asInt();
	this->create_respond_thr(res_thr,"GateMonitor::respond",false);

	//account check thread
	int chk_thr = this->cfg()["account_thr"].asInt();
	this->create_thread(chk_thr,this,GateMonitor::run_account_chk,
			"GateMonitor::run_account_chk",false);

	this->reactor(Reactor::instance());
	if(NULL==this->reactor()->implement())
	{
		this->reactor()->implement(new EpollReactor(10000,102400));
	}

	this->routine_timer_.monitor = this;
	this->routine_timer_.reactor(this->reactor());
	return 0;
}

int GateMonitor::start()
{
	super::start();

	int port = this->cfg()["port"].asInt();

	if(this->acceptor_->open(port,this->reactor())!=0)
	{
		return -1;
	}

	this->conn_dircserver();
	this->routine_timer_.schedule_timer(TimeValue(5),TimeValue(5));

#ifndef DEBUG

	this->reactor()->run_reactor_loop();
	this->wait_thread_exit();
#endif
	return 0;
}

GateMonitor::GatePlayerPool* GateMonitor::player_pool(void)
{
	return &this->player_pool_;
}

int GateMonitor::stop(void)
{
	LOG((INFO_LOG,"GateMonitor::stop"));
	this->reactor()->end_reactor_loop();
	//this->eng_client_->stop();
	return super::stop();
}

int GateMonitor::run_account_chk(void *p)
{
	GateMonitor *monitor = (GateMonitor*)p;
	if(NULL==monitor)
	{
		return 0;
	}
	return monitor->run_account_chk();
}

int GateMonitor::run_account_chk(void)
{
	while(true)
	{
		if(this->reactor()->run_status()==2)
		{
			LOG((INFO_LOG,"GateMonitor::run_account_chk exit"));
			return 0;
		}
		MessageBlock *mb = NULL;
		this->chk_account_list_->get_obj((void*&)mb);
		if (NULL != mb)
		{
			this->process_account_chk(mb);
			this->msg_pool()->push(mb);
		}
		else
		{
			usleep(1000);
		}
	}
	return 0;
}

//int GateMonitor::run_request_loop(void *p)
//{
//	GateMonitor *monitor = (GateMonitor*)p;
//	if(NULL==monitor)
//	{
//		return 0;
//	}
//	return monitor->run_request_loop();
//}
//
//int GateMonitor::run_respond_loop(void *p)
//{
//	GateMonitor *monitor = (GateMonitor*)p;
//	if(NULL==monitor)
//	{
//		return 0;
//	}
//	return monitor->run_respond_loop();
//}

int GateMonitor::run_request_loop(void)
{
	while(true)
	{
		if(this->reactor()->run_status()==2)
		{
			LOG((INFO_LOG,"GateMonitor::run_request_loop exit"));
			return 0;
		}

		MessageBlock *mb = NULL;
		this->request_mb_pop(mb);
		if (NULL != mb)
		{
			this->process_request(mb);
			this->msg_pool()->push(mb);
		}
		else
		{
			usleep(1000);
		}
	}
	return 0;
}

int GateMonitor::run_respond_loop(void)
{
	while(true)
	{
		if(this->reactor()->run_status()==2)
		{
			LOG((INFO_LOG,"GateMonitor::run_respond_loop exit"));
			return 0;
		}

		MessageBlock *mb = NULL;
		this->respond_mb_pop(mb);
		if(NULL!=mb)
		{
			this->process_respond(mb);
			this->msg_pool()->push(mb);
		}
		else
		{
			usleep(1000);
		}
	}
	return 0;
}

/*
 * id: befor login id mean svc_id,otherwise means roleid
 */
int GateMonitor::respond_succ_to_client(int id,int cmd,MessageBlock *mb)
{
	MessageBlock *mb_send = NULL;
	if(this->msg_pool()->pop(mb_send)==0)
	{
		*mb_send << id << cmd << 0 ;
		if(NULL!=mb)
		{
			mb_send->write(mb->rd_ptr(),mb->data_len());
		}
		this->respond_mb_push(mb_send);
	}
	return 0;
}

/*
 * id: before login id mean svc_id,otherwise means roleid
 */
int GateMonitor::respond_error_to_client(int id,int cmd,int error,MessageBlock *mb)
{
	MessageBlock *mb_send = NULL;
	if(this->msg_pool()->pop(mb_send)==0)
	{
		*mb_send << id << cmd << error;
		if(NULL!=mb)
		{
			mb_send->write(mb->rd_ptr(),mb->data_len());
		}
		this->respond_mb_push(mb_send);
	}
	return 0;
}

int GateMonitor::process_respond(MessageBlock *mb)
{
	int roleid =0,cmd =0;
	*mb >> roleid >>cmd;
	switch(cmd)
	{
		case INNER_E2G_ENTERROOM:
		case INNER_E2G_EXITROOM:
		case INNER_E2G_LOGOUT:
		case INNER_E2G_ERROR_INFO:
		case INNER_E2G_SWITCHROOM:
		{
			return this->process_engine_request(cmd,roleid,mb);
		}
		default:
		{
			break;
		}
	}
	return this->process_send_out(cmd,roleid,mb);
}

int GateMonitor::process_send_out(int cmd,int roleid,MessageBlock *mb)
{
	//int svc = roleid;
	LockGuardReturn(RT_MUTEX, lock, this->rt_mutex_, -1);
	GatePlayer *player = this->find_player(roleid);
	if (NULL == player )//&& !(RETURN_CREATE_ROLE == cmd || RETURN_LOG_IN == cmd))
	{
		LOG((ERROR_LOG,"GateMonitor::process_respond-->[role:%d][cmd:%d]",roleid,cmd));
		return -1;
	}

//	if (NULL != player)
//	{
//		svc = player->svc_id();
//	}
	GateService *gs = this->find_service(player->svc_id());
	if (NULL != gs)//&& (gs->role_id() == roleid
			//|| cmd == RETURN_CREATE_ROLE
			//|| cmd == RETURN_LOG_IN))
	{
		int len = mb->data_len();
		mb->reset_rd_ptr();
		::memcpy(mb->base_ptr(), (char*) &len, sizeof(int));
		int send_len = gs->send_msg(mb, mb->data_len());

		LOG((DEBUG_LOG,"GateMonitor::process_respond-->[cmd:%d][role:%d][send:%d]",cmd,roleid,send_len));
	}
	return 0;
}

int GateMonitor::process_engine_request(int cmd,int roleid,MessageBlock *mb)
{
	LOG((DEBUG_LOG,"GateMonitor::process_engine_request-->[cmd:%d][roleid:%d]",cmd,roleid));
	switch (cmd)
	{
		case INNER_E2G_ERROR_INFO:
		{
			return this->inner_error_info(roleid, mb);
		}
		case INNER_E2G_LOGOUT:
		{
			return this->log_out(roleid,mb);
		}
		case INNER_E2G_ENTERROOM:
		{
			return this->enter_room(roleid,mb,true);
		}
		case INNER_E2G_EXITROOM:
		{
			return this->exit_room(roleid,mb);
		}
		case INNER_E2G_SWITCHROOM:
		{
			return this->switch_room(roleid,mb,true);
		}
		default:
			break;
	}
	return 0;
}

int GateMonitor::process_request(MessageBlock *mb)
{
	int svc_id = 0,role_id=0, cmd = 0,remain=0;
	*mb >> svc_id >> role_id >>cmd >> remain;

	GateService *gs = this->find_service(svc_id);
	if (NULL==gs)
	{
		LOG((ERROR_LOG,"GateMonitor::process_request-->can't find svc"));
		return -1;
	}

	if(cmd ==ENGINE_REGISTER)
	{
		return this->engine_register(gs,mb);
	}
	else if(cmd==RETURN_DIRCSERVER_LOGIN)
	{
		LOG((INFO_LOG,"GateMonitor::process_request [gate:%d]register dirc[%s:%d][result:%d]",
				this->gate_id(),this->dircserver_->peer_addr(),this->dircserver_->peer_port(),remain));
		if(remain==0)
		{
			this->register_dirc_ = true;
		}
		return 0;
	}
	else if(cmd==RETURN_DIRCSERVER_UPDATE)
	{
		return this->register_room_return(mb);
	}

	if(role_id==0 && !(RETURN_DBSERVER_PLAYER_LOGIN==cmd || CLIENT_LOG_IN==cmd))
	{
		LOG((ERROR_LOG,"GateMonitor::process_request-->[role:%d][cmd:%d]",role_id,cmd));
		return gs->handle_close();
	}

	int room_id = 0;
	{
		LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,-1);
		GatePlayer *player = this->find_player(role_id);
		if (role_id != 0 && NULL == player)
		{//未登录
			return gs->handle_close();
		}
		else if (NULL != player)
		{
			if(cmd==CLIENT_LOG_IN && player->is_login())
			{
				LOG((INFO_LOG,"GateMonitor::process_request-->[role:%d already login]",player->role_id()));
				return  this->respond_error_to_client(gs->unique_id(),RETURN_LOG_IN,ERROR_LOGIN_AGAIN);
			}
			room_id = player->cur_room();
		}
	}
	LOG((DEBUG_LOG,"GateMonitor::process_request-->[cmd:%d][role:%d][from:%s:%d]",
			cmd,role_id,gs->peer_addr(),gs->peer_port()));

	switch (cmd)
	{
		case RETURN_DBSERVER_PLAYER_LOGIN:
		case CLIENT_LOG_IN:
		{
			LOG((DEBUG_LOG,"GateMonitor::process_request-->login request[cmd:%d][role:%d]",cmd,role_id));
			return this->log_in(svc_id,mb);
		}
		default:
			break;
	}

	//add gate id to message block
	int gate_id = this->gate_id();
	::memcpy(mb->base_ptr(),(char*)&gate_id,sizeof(int));

	switch (cmd)
	{
		case CLIENT_ENTER_ROOM:
		{
			return this->enter_room(role_id,mb);
		}
		case CLIENT_SWITCH_ROOM:
		{
			return this->switch_room(role_id,mb);
		}
		case CLIENT_RECHARGE:
		{
			return this->recharge(svc_id,mb);
		}
		case CLIENT_QUERY_CHIPS:
		case CLIENT_EXCHANGE_CHIPS:
		{
			break;
		}
		default:
		{
			mb->reset_rd_ptr();
			return this->dispatch_request(room_id, mb);
		}
	}
	return 0;
}
